package s3client

import (
	s3action "gitee.com/lsy007/s3client/model"
	"github.com/golang/protobuf/proto"
	"github.com/pquerna/ffjson/ffjson"
	"strconv"
)

func (c *Client) ExistBucket(bucket string) (has bool, err error) {
	reply, err := c.Request(s3action.Model{Func: "ExistBucket", Bucket: bucket})
	return reply.Has, err
}

func (c *Client) ExistObject(bucket, key string) (has bool, err error) {
	reply, err := c.Request(s3action.Model{Func: "ExistObject", Bucket: bucket, Key: key})
	return reply.Has, err
}

func (c *Client) ExistObjects(bucket string, keys []string) (num int, err error) {
	// 1. 编码keys
	byteData, err := ffjson.Marshal(&keys)
	if err != nil {
		return num, err
	}
	// 2. 组装 model
	model := s3action.Model{Func: "ExistObjects", Bucket: bucket, Data: byteData}
	reply, err := c.Request(model)
	if err != nil {
		return num, err
	}
	// 3. 解析结果并返回
	return strconv.Atoi(reply.Value)
}

func (c *Client) CreateBucket(bucket, acl string) (err error) {
	_, err = c.Request(s3action.Model{Func: "CreateBucket", Bucket: bucket, Acl: acl})
	return
}

func (c *Client) ListBuckets() (list []*BucketResult, err error) {
	// 1. 执行请求
	reply, err := c.Request(s3action.Model{Func: "ListBuckets"})
	if err != nil {
		return list, err
	}
	// 2.解析结果
	var l s3action.ListBucketResult
	if err = proto.Unmarshal(reply.Data, &l); err != nil {
		return
	}
	// 3. 获取本地可操作列表
	list = make([]*BucketResult, 0)
	for _, v := range l.List {
		list = append(list, &BucketResult{Name: v.Name, Date: v.CreateDate})
	}
	return
}

func (c *Client) ListObjects(data *ListObjectInput, bucket ...string) (list []*ObjectResult, has bool, err error) {
	// 1. 编码查询条件
	byteData, err := proto.Marshal(&s3action.ListObjectInput{
		Delimiter: data.Delimiter,
		Marker:    data.Marker,
		MaxKeys:   data.MaxKeys,
		Prefix:    data.Prefix,
	})
	if err != nil {
		return list, has, err
	}
	// 2. 组装model
	model := s3action.Model{Func: "ListObjects", Data: byteData}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 3. 执行请求
	reply, err := c.Request(model)
	if err != nil {
		return list, has, err
	}
	// 4. 判断是否存在结果
	if !reply.Has {
		return list, has, nil
	}
	// 5. 解析结果
	var l s3action.ListObjectResult
	if err = proto.Unmarshal(reply.Data, &l); err != nil {
		return
	}
	// 6. 获取本地可操作列表
	list = make([]*ObjectResult, 0)
	for _, v := range l.List {
		list = append(list, &ObjectResult{
			Key:          v.Key,
			Size:         v.Size,
			Etag:         v.Etag,
			LastModified: v.LastModified,
			StorageClass: v.StorageClass,
		})
	}
	return list, true, nil
}

func (c *Client) PutObjectPreSign(data *PutObjectInput, bucket ...string) (url string, err error) {
	// 1. 编码上传预签名信息
	byteData, err := proto.Marshal(&s3action.PutObjectInput{
		Acl:           data.Acl,
		StorageClass:  data.StorageClass,
		Tagging:       data.Tagging,
		Expires:       data.Expires,
		ContentType:   data.ContentType,
		ContentLength: data.ContentLength,
		ContentMd5:    data.ContentMd5,
	})
	if err != nil {
		return url, err
	}
	// 2. 组装model
	model := s3action.Model{Func: "PutObjectPreSign", Key: data.Key, Ttl: data.Ttl, Data: byteData}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 3. 执行请求
	reply, err := c.Request(model)
	if err != nil {
		return url, err
	}
	return reply.Value, nil
}

// ttl = 0 时，默认有效时长为 300 s
func (c *Client) GetObjectPreSign(key string, ttl int64, bucket ...string) (url string, err error) {
	// 1. 组装 model
	model := s3action.Model{Func: "GetObjectPreSign", Key: key, Ttl: ttl}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 2. 执行请求
	reply, err := c.Request(model)
	if err != nil {
		return url, err
	}
	return reply.Value, nil
}

// ttl = 0 时，默认有效时长为 300 s
func (c *Client) BatchGetObjectPreSign(keys []string, ttl int64, bucket ...string) (list map[string]string, err error) {
	// 1. 编码keys
	byteData, err := ffjson.Marshal(&keys)
	if err != nil {
		return list, err
	}
	// 2. 组装 model
	model := s3action.Model{Func: "BatchGetObjectPreSign", Data: byteData, Ttl: ttl}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 3. 执行请求
	reply, err := c.Request(model)
	if err != nil {
		return list, err
	}
	// 4. 解析结果
	list = make(map[string]string, 0)
	err = ffjson.Unmarshal(reply.Data, &list)
	return
}

func (c *Client) DeleteObject(key string, bucket ...string) (err error) {
	// 1. 组装 model
	model := s3action.Model{Func: "DeleteObject", Key: key}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 2. 执行请求
	_, err = c.Request(model)
	return
}

func (c *Client) CopyObject(srcKey, dstKey string, bucket ...string) (err error) {
	// 1. 组装 model
	model := s3action.Model{Func: "CopyObject", Key: srcKey, DstKey: dstKey}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 2. 执行请求
	_, err = c.Request(model)
	return
}

func (c *Client) MoveObject(srcKey, dstKey string, bucket ...string) (err error) {
	// 1. 组装 model
	model := s3action.Model{Func: "MoveObject", Key: srcKey, DstKey: dstKey}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 2. 执行请求
	_, err = c.Request(model)
	return
}

func (c *Client) PutBucketLifecycle(rules []*LifecycleRule, bucket ...string) (err error) {
	// 1. 编码生命周期规则数据
	l := make([]*s3action.LifecycleRule, 0)
	for _, r := range rules {
		status := "Enabled"
		if !r.Status {
			status = "Disabled"
		}
		l = append(l, &s3action.LifecycleRule{
			Id:         r.Id,
			Prefix:     r.Prefix,
			Status:     status,
			Expiration: r.Expiration,
		})
	}
	byteData, err := proto.Marshal(&s3action.ListLifecycleRule{List: l})
	if err != nil {
		return err
	}
	// 2. 组装 model
	model := s3action.Model{Func: "PutBucketLifecycle", Data: byteData}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 3. 执行请求
	_, err = c.Request(model)
	return
}

func (c *Client) GetBucketLifecycleConfiguration(bucket ...string) (list []*LifecycleRule, has bool, err error) {
	// 1. 组装 model
	model := s3action.Model{Func: "GetBucketLifecycleConfiguration"}
	if bucket != nil {
		model.Bucket = bucket[0]
	}
	// 2. 执行请求
	reply, err := c.Request(model)
	if err != nil {
		return list, has, err
	}
	if !reply.Has {
		return list, false, nil
	}
	// 3. 解析响应结果
	var l s3action.ListLifecycleRule
	if err = proto.Unmarshal(reply.Data, &l); err != nil {
		return list, false, err
	}
	// 4. 响应本地可操作列表
	list = make([]*LifecycleRule, 0)
	for _, v := range l.List {
		status := true
		if v.Status == "Disabled" {
			status = true
		}
		list = append(list, &LifecycleRule{
			Id:         v.Id,
			Prefix:     v.Prefix,
			Status:     status,
			Expiration: v.Expiration,
		})
	}
	return list, true, nil
}
